/* eslint-disable no-param-reassign */
import { defineComponent, onMounted, PropType, ref } from 'vue';
import { useNamespace } from '@ibiz-template/vue3-util';
import RecursiveTreeNode from '../recursive-tree-node/recursive-tree-node';
import { IRecursiveTreeNode, NodeDropType } from '../recursive-tree-util';
import './recursive-tree.scss';

export const RecursiveTree = defineComponent({
  name: 'RecursiveTree',
  props: {
    load: {
      type: Function as PropType<
        (
          _item: IRecursiveTreeNode,
          _callback: (nodes: IData[]) => void,
        ) => Promise<void>
      >,
    },
    allowDrop: {
      type: Function as PropType<
        (
          _draggingNode: IRecursiveTreeNode,
          _dropNode: IRecursiveTreeNode,
          _type: 'inner' | 'prev' | 'next',
        ) => boolean
      >,
    },
    allowDrag: {
      type: Function as PropType<
        (_draggingNode: IRecursiveTreeNode) => boolean
      >,
    },
  },
  emits: {
    nodeDrop: (
      _draggingNode: IRecursiveTreeNode,
      _dropNode: IRecursiveTreeNode,
      _dropType: NodeDropType,
    ) => true,
    nodeExpand: (_node: IRecursiveTreeNode) => true,
    nodeCollapse: (_node: IRecursiveTreeNode) => true,
  },
  setup(props, { emit }) {
    const ns = useNamespace(`org-recursive-tree`);
    const treeData = ref<IRecursiveTreeNode[]>([]);
    const expandedKeys = ref<Array<string | number>>([]);

    /**
     * @description 查询菜单项模型
     *
     * @export
     * @param {string} _id 菜单标识
     * @param {IAppMenuItem[]} items 菜单数据集合
     * @return {*}  {(IAppMenuItem | undefined)}
     */
    function findNodeItem(
      _id: string | number,
      items: IRecursiveTreeNode[],
    ): IRecursiveTreeNode | undefined {
      let temp: IRecursiveTreeNode | undefined;
      if (items) {
        items.some((item: IRecursiveTreeNode): boolean => {
          if (!item.id) return true;
          if (item.id === _id) {
            temp = item;
            return true;
          }
          if (item.children && item.children.length > 0) {
            temp = findNodeItem(_id, item.children);
            if (!temp) {
              return false;
            }
            return true;
          }
          return false;
        });
      }
      return temp;
    }

    /** 将传入的 data 转换为内部 treeData（增加扩展字段） */
    const transformData = (
      item: IData,
      parent: IRecursiveTreeNode | null = null,
    ): IRecursiveTreeNode => {
      const level = parent ? parent.level + 1 : 0;
      const node: IRecursiveTreeNode = {
        id: item._id,
        label: item._text,
        level,
        parent,
        data: item,
        loaded: false,
        loading: false,
      };
      return node;
    };
    const handleChildren = (
      data: IData[],
      parent: IRecursiveTreeNode | null = null,
    ): IRecursiveTreeNode[] => {
      return data.map(item => transformData(item, parent));
    };

    /** 懒加载节点数据 */
    const loadNodeData = async (_node: IRecursiveTreeNode): Promise<void> => {
      if (!props.load || _node.loading || _node.isLeaf) return;

      _node.loading = true;
      await props.load(_node, (children: IData[]) => {
        _node.children = handleChildren(children, _node);
        _node.loaded = true;
        _node.loading = false;
      });
    };

    const load = async (): Promise<void> => {
      treeData.value = [transformData({ _id: 'root' })];
      await loadNodeData(treeData.value[0]);
    };

    const refresh = async (): Promise<void> => {
      await load();
      expandedKeys.value = [];
    };

    onMounted(() => {
      load();
    });

    /** 展开节点时触发懒加载 */
    const handleNodeExpand = async (
      _node: IRecursiveTreeNode,
    ): Promise<void> => {
      const node = findNodeItem(_node.id, treeData.value);
      if (!node) {
        return;
      }

      await loadNodeData(node);

      const _tag = node.id;
      const expandedKeysSet = new Set(expandedKeys.value);
      const expanded =
        !expandedKeysSet.has(_tag) && node.children && node.children.length > 0;

      // 移除当前节点及其所有子节点的展开状态
      const removeChildKeys = (childNode: IRecursiveTreeNode): void => {
        if (expandedKeysSet.has(childNode.id)) {
          expandedKeysSet.delete(childNode.id);
          emit('nodeCollapse', childNode);
        }
        if (childNode.children) {
          childNode.children.forEach(removeChildKeys);
        }
      };

      // 清除同层级下的所有key
      if (node.parent && node.parent.children) {
        node.parent.children.forEach(_item => {
          if (expandedKeysSet.has(_item.id)) removeChildKeys(_item);
        });
      }

      // 当重复点击的时候，清空当前点击的子展开项及所有子展开的所有子点击项
      if (expanded) {
        expandedKeysSet.add(_tag);
        emit('nodeExpand', node);
      } else {
        removeChildKeys(node);
      }
      expandedKeys.value = Array.from(expandedKeysSet);
    };

    const handleNodeDrop = (
      _draggingNode: IRecursiveTreeNode,
      _dropNode: IRecursiveTreeNode,
      _dropType: NodeDropType,
    ): void => {
      emit('nodeDrop', _draggingNode, _dropNode, _dropType);
    };

    const dropAfterActive = async (
      _draggingNode: IRecursiveTreeNode,
      _dropNode: IRecursiveTreeNode,
    ): Promise<void> => {
      await handleNodeExpand(_dropNode);
      if (expandedKeys.value.indexOf(_dropNode.id) === -1) {
        expandedKeys.value.push(_dropNode.id);
        emit('nodeExpand', _dropNode);
      }

      // 更新老数据
      setTimeout(() => {
        const _node = findNodeItem(_draggingNode.id, treeData.value);
        if (_node) {
          const _index = _node.parent?.children?.findIndex(
            _item => _item.id === _node.id,
          );
          if (_index !== undefined && _index !== -1)
            _node.parent?.children?.splice(_index, 1);
        }
      }, 100);
    };

    return {
      ns,
      treeData,
      expandedKeys,
      refresh,
      handleNodeExpand,
      transformData,
      handleNodeDrop,
      dropAfterActive,
    };
  },
  render() {
    return (
      <div class={this.ns.b()}>
        {this.treeData.length > 0 && (
          <div class={this.ns.e('content')}>
            {this.treeData.map((node, index) => (
              <RecursiveTreeNode
                node={node}
                separator={index !== 0}
                expandedKeys={this.expandedKeys}
                allow-drop={this.allowDrop}
                allow-drag={this.allowDrag}
                onNodeDrop={this.handleNodeDrop}
              >
                {{
                  default: (params: { data: IData }) =>
                    this.$slots.default?.(params),
                  active: (params: { data: IData }) =>
                    this.$slots.active?.(params),
                }}
              </RecursiveTreeNode>
            ))}
          </div>
        )}
      </div>
    );
  },
});

export default RecursiveTree;
